﻿using ClosedXML.Excel;
using HandyControl.Tools.Extension;
using ICSharpCode.AvalonEdit;
using MiniExcelLibs;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using Clipboard = System.Windows.Clipboard;
using Path = System.IO.Path;
using TextBox = HandyControl.Controls.TextBox;

namespace HandyControlDemo.UserControl
{
    /// <summary>
    /// PropertyGrid.xaml 的交互逻辑
    /// </summary>
    public partial class Codes
    {
        public static Codes _code;

        public Codes()
        {
            InitializeComponent();


            _code = this;
        }

        private void TextBoxComplete_TextChange()
        {

        }

        private void TextBoxComplete_SelectComBox()
        {

        }


        private void Selection_Changed(object sender, SelectionChangedEventArgs e)
        {



        }

        private void ObjTextChanged(object sender, TextChangedEventArgs e)
        {
            //isloding.Show();
            var txt = sender as TextBox;
            if (txt.Name == "Use")
            {


            }
            //_o.GetType().GetProperty(txt.Name).SetValue(_o, txt.Text);

            //isloding.Hide();
        }

        private void MenuItem1_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var check = (sender as MenuItem).Header.ToString();
                if (check == "全选") TextEditor.SelectAll();
                else if (check == "复制") Clipboard.SetText(TextEditor.SelectedText);
                else if (check == "复制所有内容") System.Windows.Clipboard.SetText(TextEditor.Text);
            }
            catch { }

        }


        private async void add_Click(object sender, RoutedEventArgs e)
        {
            // 显示等待动画

            // ① 异步检查
            string txtpath1 = txtpath.Text;
            string jsonpath1 = jsonpath.Text;
            string filecounts1 = filecounts.Text;
            int fileconutss = 10;
            if (int.TryParse(filecounts1, out fileconutss))
            {
                // 是整数，n 即为解析后的值
            }

            var dd = await 选手检查器.开始检查(txtpath1, jsonpath1, fileconutss);
            var liststus = new List<PersonDto>();
            if (dd != null)
            {
                isloding.Show();
                string file = Path.Combine(
                               AppDomain.CurrentDomain.BaseDirectory,
                               "模板",
                               "选手信息.xlsx");

                if (!File.Exists(file))
                {
                    HandyControl.Controls.Growl.Error("找不到文件" + file);
                    return;
                }

                // 3. 一行代码读成 List<PersonDto>
                liststus = MiniExcel.Query<PersonDto>(file).ToList();
                // 一行 LINQ 完成赋值
                dd.ForEach(c =>
                {
                    var dto = liststus.FirstOrDefault(p => p.联系电话 == c.选手名);
                    if (dto != null) { c.序号 = dto.序号.ToString(); c.抽签号 = dto.抽签号; c.姓名 = dto.姓名; }
                });
                // ② 异步导出（IO 操作也放后台）
                await Task.Run(() => 选手检查器.导出并打开(dd));


                isloding.Hide();                       // 隐藏等待动画
                HandyControl.Controls.Growl.Success("检查完成，稍等 Excel 自动打开");
            }
        }
        #region 刷新列表
        /// <summary>
        /// 刷新列表
        /// </summary>

        #endregion





    }


    public class PersonDto
    {
        [MiniExcelLibs.Attributes.ExcelColumnName("序号")]
        public int 序号 { get; set; }

        [MiniExcelLibs.Attributes.ExcelColumnName("抽签号")]
        public string 抽签号 { get; set; }

        [MiniExcelLibs.Attributes.ExcelColumnName("姓名")]
        public string 姓名 { get; set; }

        [MiniExcelLibs.Attributes.ExcelColumnName("联系电话")]
        public string 联系电话 { get; set; }

        [MiniExcelLibs.Attributes.ExcelColumnName("IP")]
        public string IP { get; set; }
    }







    public class 选手检查器
    {
        #region 基本信息
        public string 选手名 { get; private set; } = "";
        #endregion

        #region 检查字段（字符串，✔✘）
        public string Json文件夹是否存在检查 { get; private set; } = "未执行";
        public string Txt文件夹是否存在检查 { get; private set; } = "未执行";

        public string Json转Txt后文件数量是否一致检查 { get; private set; } = "未执行";

        public string Json文件夹文件后缀检查 { get; private set; } = "未执行";
        public string Txt文件夹文件后缀检查 { get; private set; } = "未执行";

        public string Json_Txt文件名一一对应检查 { get; private set; } = "未执行";

        public string 内容比对检查 { get; private set; } = "未执行";
        #endregion

        #region 结果
        public string 是否通过 { get; private set; } = "未执行";
        public string 结果详情 { get; private set; } = "未执行";
        //public string 完成时间 { get; private set; } = "";
        #endregion
        public string 序号 { get; set; }
        public string 抽签号 { get; set; }
        public string 姓名 { get; set; }
        #region 静态入口（弹窗）
        public static async Task<List<选手检查器>>? 开始检查(string txtpath1, string jsonpath1, int fileconutss)
        {
            string? root = 让用户选文件夹();
            if (string.IsNullOrEmpty(root)) return null;

            return await Task.Run(() =>
            {
                return Directory.GetDirectories(root)
                             .Select(路径 =>
                             {
                                 var c = new 选手检查器();
                                 c.执行检查(路径, txtpath1, jsonpath1, fileconutss);
                                 return c;
                             })
                             .ToList();
            });

        }

        private static string? 让用户选文件夹()
        {
            using var dlg = new FolderBrowserDialog { Description = "请选择选手根目录" };
            return dlg.ShowDialog() == DialogResult.OK ? dlg.SelectedPath : null;
        }
        #endregion

        #region 执行检查（一步失败立即停，详情具体到文件）
        private void 执行检查(string 选手路径, string txtpath1, string jsonpath1, int fileconutss)
        {
            选手名 = Path.GetFileName(选手路径);

            /*---- 1. 文件夹存在 ----*/
            var jsonDir = Path.Combine(选手路径, jsonpath1);
            var txtDir = Path.Combine(选手路径, txtpath1);

            Json文件夹是否存在检查 = Directory.Exists(jsonDir) ? "✔" : "✘";
            Txt文件夹是否存在检查 = Directory.Exists(txtDir) ? "✔" : "✘";

            if (Json文件夹是否存在检查 == "✘" || Txt文件夹是否存在检查 == "✘")
            {
                结果(false, "缺少Json或Txt文件夹");
                return;
            }

            /*---- 2. 数量 ----*/
            var jsonFiles = Directory.GetFiles(jsonDir, "*", SearchOption.TopDirectoryOnly);
            var txtFiles = Directory.GetFiles(txtDir, "*", SearchOption.TopDirectoryOnly);
            if (jsonFiles.Length < fileconutss || txtFiles.Length < fileconutss) Json转Txt后文件数量是否一致检查 = "✘";
            else Json转Txt后文件数量是否一致检查 = jsonFiles.Length == txtFiles.Length ? "✔" : "✘";
            if (Json转Txt后文件数量是否一致检查 == "✘")
            {
                结果(false, $"文件数量不一致或文件数量不满足要求：Json={jsonFiles.Length}  Txt={txtFiles.Length}");
                return;
            }

            /*---- 3. 后缀 ----*/
            var badJson = jsonFiles.Where(f => !f.EndsWith(".json", StringComparison.OrdinalIgnoreCase)).Select(System.IO.Path.GetFileName).ToList();
            var badTxt = txtFiles.Where(f => !f.EndsWith(".txt", StringComparison.OrdinalIgnoreCase)).Select(Path.GetFileName).ToList();

            Json文件夹文件后缀检查 = badJson.Count == 0 ? "✔" : "✘";
            Txt文件夹文件后缀检查 = badTxt.Count == 0 ? "✔" : "✘";

            if (badJson.Any() || badTxt.Any())
            {
                var sb = new StringBuilder("后缀错误：");
                if (badJson.Any()) sb.Append($"Json文件夹内含非.json文件：{string.Join("、", badJson)}  ");
                if (badTxt.Any()) sb.Append($"Txt文件夹内含非.txt文件：{string.Join("、", badTxt)}");
                结果(false, sb.ToString());
                return;
            }

            /*---- 4. 文件名对应（除后缀）----*/
            var jsonNames = jsonFiles.Select(f => Path.GetFileNameWithoutExtension(f)).ToHashSet();
            var txtNames = txtFiles.Select(f => Path.GetFileNameWithoutExtension(f)).ToHashSet();

            var onlyInJson = jsonNames.Except(txtNames).ToList();
            var onlyInTxt = txtNames.Except(jsonNames).ToList();

            if (onlyInJson.Any() || onlyInTxt.Any())
            {
                var sb = new StringBuilder("文件名未完全对应（除后缀）：");
                if (onlyInJson.Any()) sb.Append($"Json侧多出：{string.Join("、", onlyInJson)}  ");
                if (onlyInTxt.Any()) sb.Append($"Txt侧多出：{string.Join("、", onlyInTxt)}");
                Json_Txt文件名一一对应检查 = "✘";
                结果(false, sb.ToString());
                return;
            }
            Json_Txt文件名一一对应检查 = "✔";

            /*---- 5. 内容比对（同名 txt vs json 零文件双检）----*/
            try
            {
                内容比对检查 = "✘";                       // 先占位

                var classNames = new List<string> { "anti-corrosion area", "full", "not run out" };

                // 只比对同名文件（后缀不同）
                foreach (var txt in txtFiles)
                {
                    var nameWithoutExt = Path.GetFileNameWithoutExtension(txt);
                    var json = jsonFiles.FirstOrDefault(f =>
                                Path.GetFileNameWithoutExtension(f).Equals(nameWithoutExt,
                                                                           StringComparison.OrdinalIgnoreCase));
                    if (json == null)
                    {
                        内容比对检查 = "✘";
                        结果(false, $"缺少对应 JSON：{nameWithoutExt}.json");
                        return;
                    }

                    string result = 双检(File.ReadAllText(txt),
                                                        File.ReadAllText(json),
                                                        classNames);
                    if (result != "通过")
                    {
                        内容比对检查 = "✘";
                        结果(false, $"{nameWithoutExt}：{result}");
                        return;
                    }
                }

                内容比对检查 = "✔";
                结果(true, "通过");
            }
            catch (Exception ex)
            {
                内容比对检查 = "✘";
                结果(false, $"比对异常：{ex.Message}");
            }
        }
        #endregion

        #region 辅助
        private void 结果(bool 通过, string 描述)
        {
            是否通过 = 通过 ? "✔" : "✘";
            结果详情 = 描述;
            // 完成时间 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }

        private static bool CompareTxtAndJson(string[] txtFiles, string[] jsonFiles, out string 描述)
        {
            // TODO: 你的比对规则
            描述 = "";
            return true;
        }
        #endregion





        /// <summary>
        /// 一站式：txt 合法性 + 能否 100% 还原回 JSON
        /// </summary>
        /// <param name="txtContent">YOLO 标注文本（多行）</param>
        /// <param name="originalJsonContent">原图 labelme json 全文</param>
        /// <param name="classNames">训练时类别顺序表</param>
        /// <returns>“通过” 或具体错误描述</returns>
        public static string 双检(string txtContent, string originalJsonContent, IReadOnlyList<string> classNames)
        {
            if (string.IsNullOrWhiteSpace(txtContent)) return "txt 为空";
            if (string.IsNullOrWhiteSpace(originalJsonContent)) return "原 JSON 为空";


            var lines = txtContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);


            #region 2. 逐行还原并与 JSON 比对
            using var jsonDoc = JsonDocument.Parse(originalJsonContent);
            var root = jsonDoc.RootElement;
            int width = root.GetProperty("imageWidth").GetInt32();
            int height = root.GetProperty("imageHeight").GetInt32();
            var jsonShapes = root.GetProperty("shapes").EnumerateArray().ToList();

            if (lines.Length != jsonShapes.Count)
                return $"shapes数量不一致：txt {lines.Length} 条 vs JSON {jsonShapes.Count} 条";

            for (int i = 0; i < lines.Length; i++)
            {
                var parts = lines[i].Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                int clsId = int.Parse(parts[0], CultureInfo.InvariantCulture);
                int nPts = (parts.Length - 1) / 2;
                var xs = Step(parts.Skip(1), 2).Select(s => double.Parse(s, CultureInfo.InvariantCulture)).ToArray();
                var ys = Step(parts.Skip(2), 2).Select(s => double.Parse(s, CultureInfo.InvariantCulture)).ToArray();

                // 还原像素坐标（保留 3 位小数，与 Python 导出一致）
                var restored = new List<List<double>>(nPts);
                for (int k = 0; k < nPts; k++)
                    restored.Add(new List<double>
                    {
                        Math.Round(xs[k] * width, 3),
                        Math.Round(ys[k] * height, 3)
                    });

                var jsonShape = jsonShapes[i];
                // 1. 类别
                //string jsonLabel = jsonShape.GetProperty("label").GetString()!;
                //if (jsonLabel != classNames[clsId - 1])
                //    return $"第 {i + 1} 行：类别不一致（txt {classNames[clsId]} vs JSON {jsonLabel}）";

                // 2. 点数
                var jsonPts = jsonShape.GetProperty("points").EnumerateArray()
                                       .Select(p => p.EnumerateArray().ToList())
                                       .ToList();
                if (restored.Count != jsonPts.Count)
                    return $"第 {i + 1} 行：点数不一致（txt {restored.Count} vs JSON {jsonPts.Count}）";

                // 3. 坐标值（逐点比对）
                for (int k = 0; k < restored.Count; k++)
                {
                    if (Math.Abs(restored[k][0] - jsonPts[k][0].GetDouble()) > 1e-3 ||
                        Math.Abs(restored[k][1] - jsonPts[k][1].GetDouble()) > 1e-3)
                        return $"第 {i + 1} 行 第 {k + 1} 点：坐标偏差（txt [{restored[k][0]},{restored[k][1]}] vs JSON [{jsonPts[k][0]},{jsonPts[k][1]}]）";
                }

                //// 4. shape_type（可选校验）
                //string shapeType = jsonShape.GetProperty("shape_type").GetString()!;
                //string expectType = nPts == 2 ? "rectangle" : "polygon";
                //if (shapeType != expectType)
                //    return $"第 {i + 1} 行：形状类型不一致（txt 推导 {expectType} vs JSON {shapeType}）";
            }
            #endregion

            return "通过";
        }

        /* 扩展：Step 方法（NuGet System.Linq 没有，自己写） */
        private static IEnumerable<T> Step<T>(IEnumerable<T> source, int step)
        {
            int i = 0;
            foreach (var item in source)
            {
                if (i % step == 0) yield return item;
                i++;
            }
        }
























        public static void 导出并打开(List<选手检查器> list)
        {
            var exeDir = AppDomain.CurrentDomain.BaseDirectory;
            string fileName;
            do
            {
                fileName = Path.Combine(exeDir, $"数据验证校验{DateTime.Now:yyyyMMddHHmmss}.xlsx");
            }
            while (File.Exists(fileName));

            导出(list, fileName);

            /*==== 生成后自动打开 ====*/
            // 先释放文件句柄（ClosedXML 已 Dispose），再用系统默认程序打开
            var psi = new ProcessStartInfo(fileName)
            {
                UseShellExecute = true,   // 必须 true，才能调系统关联程序
                Verb = "open"             // 显式指定“打开”
            };
            Process.Start(psi);
        }

        private static void 导出(List<选手检查器> list, string 保存路径)
        {
            using (var wb = new XLWorkbook())
            {
                var ws = wb.Worksheets.Add("检查结果");
                var props = typeof(选手检查器)
                           .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                           .Where(p => p.CanRead)
                           .ToArray();

                // 1. 表头
                for (int i = 0; i < props.Length; i++)
                    ws.Cell(1, i + 1).Value = props[i].Name;
                ws.Row(1).Style.Alignment.SetWrapText(true);          // 1. 自动换行
                ws.Columns().AdjustToContents();                      // 2. 按内容自动列宽
                ws.Row(1).Height = 40;                                // 3. 行高再留足（可再加大）
                ws.Row(1).Style
                         .Font.SetBold().Font.SetFontSize(10)
                         .Font.SetFontColor(XLColor.White)
                         .Fill.SetBackgroundColor(XLColor.FromHtml("#2F75B5"))
                         .Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
                         .Alignment.SetVertical(XLAlignmentVerticalValues.Center);

                // 2. 数据 + 着色
                for (int r = 0; r < list.Count; r++)
                {
                    for (int c = 0; c < props.Length; c++)
                    {
                        var cell = ws.Cell(r + 2, c + 1);
                        var val = props[c].GetValue(list[r])?.ToString() ?? "";
                        cell.Value = val;

                        if (val == "✔") cell.Style.Font.FontColor = XLColor.Green;
                        if (val == "✘") cell.Style.Font.FontColor = XLColor.Red;

                        if (props[c].Name == "是否通过")
                        {
                            if (val == "通过") cell.Style.Font.FontColor = XLColor.Green;
                            if (val == "不通过") cell.Style.Font.FontColor = XLColor.Red;
                        }
                        if (props[c].Name == "结果详情" && val != "通过")
                            cell.Style.Font.FontColor = XLColor.Red;
                    }
                }

                // 3. 边框
                var range = ws.RangeUsed();
                range.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
                range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;

                ws.Columns().AdjustToContents();
                wb.SaveAs(保存路径);
            }   // 文件句柄在此已释放
        }

    }

}
